/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.pig4cloud.pigx.strategy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.redis.constants.CommonRedisConstants;
import com.pig4cloud.pigx.common.customize.redis.constants.RedisKeyEnum;
import com.pig4cloud.pigx.common.customize.redis.utils.RedisUtils;
import com.pig4cloud.pigx.strategy.bo.StrategyTaskContractCacheBO;
import com.pig4cloud.pigx.strategy.bo.StrategyTaskContractCalculateProfitBO;
import com.pig4cloud.pigx.strategy.dto.StrategyContractErrorResolveDTO;
import com.pig4cloud.pigx.strategy.dto.StrategyTaskContractConditionDTO;
import com.pig4cloud.pigx.strategy.entity.StrategyContract;
import com.pig4cloud.pigx.strategy.entity.StrategyTaskContract;
import com.pig4cloud.pigx.strategy.enums.StrategyTaskContractEnum;
import com.pig4cloud.pigx.strategy.executor.contract.order.StrategyOrderContractHandlerManager;
import com.pig4cloud.pigx.strategy.executor.contract.task.StrategyTaskContractHandler;
import com.pig4cloud.pigx.strategy.executor.contract.task.supporter.StrategyTaskContractCalculator;
import com.pig4cloud.pigx.strategy.mapper.StrategyContractMapper;
import com.pig4cloud.pigx.strategy.mapper.StrategyTaskContractMapper;
import com.pig4cloud.pigx.strategy.mapstruct.StrategyTaskContractStruct;
import com.pig4cloud.pigx.strategy.service.StrategyTaskContractService;
import com.pig4cloud.pigx.strategy.util.StrategyContractUtil;
import com.pig4cloud.pigx.strategy.vo.StrategyTaskContractVO;
import com.pig4cloud.pigx.system.constant.SystemPlatformConstant;
import com.pig4cloud.pigx.system.entity.SystemProduct;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 策略任务-合约
 *
 * @since 2022-11-24 10:39:27
 */
@Service
@RequiredArgsConstructor
public class StrategyTaskContractServiceImpl extends ServiceImpl<StrategyTaskContractMapper, StrategyTaskContract> implements StrategyTaskContractService {

    private final StrategyContractUtil strategyContractUtil;
    private final RedisUtils redisUtils;
    private final StrategyTaskContractHandler strategyTaskContractHandler;
    private final StrategyContractMapper strategyContractMapper;
    private final StrategyOrderContractHandlerManager strategyOrderContractHandlerManager;

    /**
     * 分页查询
     *
     * @param page      分页数据
     * @param condition 查询条件
     * @return {@link Page <  StrategyTaskContractVO >}
     * @since 2022/11/27 17:19
     */
    @Override
    public Page<StrategyTaskContractVO> page(Page<StrategyTaskContract> page, StrategyTaskContractConditionDTO condition) {
        QueryWrapper<StrategyTaskContract> query = new QueryWrapper<>();
        query.lambda()
                .eq(StrategyTaskContract::getStrategyId, condition.getStrategyId())
                .eq(Strings.isNotBlank(condition.getStatus()), StrategyTaskContract::getStatus, condition.getStatus())
                .orderByDesc(StrategyTaskContract::getCreateTime);
        page = baseMapper.selectPage(page, query);
        List<StrategyTaskContract> list = page.getRecords();
        if (CollectionUtils.isEmpty(list)) {
            return MysqlUtil.EMPTY_PAGE;
        }

        List<StrategyTaskContractVO> voList = StrategyTaskContractStruct.INSTANCE.list2VOList(list);

        // 查询策略
        StrategyContract strategy = strategyContractMapper.selectById(condition.getStrategyId());
        SystemProduct product = null;
        BigDecimal price = null;
        if (!Objects.isNull(strategy)) {
            // 查询产品
            product = strategyContractUtil.getProductById(strategy.getProductId(), false);
            // 查询价格
            if (product.getPlatformId().equals(SystemPlatformConstant.OKX)) {
                price = redisUtils.getSymbolPrice(strategy.getSymbolName(), CommonRedisConstants.OKX + CommonRedisConstants.LAST_PRICE);
            }
        }

        // 归集任务id
        Set<Long> taskIdSet = voList.stream()
                .map(StrategyTaskContractVO::getId)
                .collect(Collectors.toSet());
        // 查询任务缓存
        List<StrategyTaskContractCacheBO> taskCacheList = redisUtils.getPersistentCache(RedisKeyEnum.STRATEGY_TASK_CONTRACT, taskIdSet);

        // 遍历
        for (StrategyTaskContractVO vo : voList) {
            // 设置产品名称
            if (!Objects.isNull(product)) {
                vo.setProductName(product.getName());
            }

            // 获取对应的任务缓存
            Optional<StrategyTaskContractCacheBO> taskCacheOpt = taskCacheList.stream()
                    .filter(i -> Objects.equals(vo.getId(), i.getId()))
                    .findFirst();
            // 任务缓存不为空
            if (taskCacheOpt.isPresent()) {
                StrategyTaskContractCacheBO taskCache = taskCacheOpt.get();

                // 价格不为空
                if (!Objects.isNull(price)) {
                    // 任务状态为运行中时，计算下次补仓价格
                    getNextAddPrice(strategy, price, vo, taskCache);

                    // 仓位不为空时，计算盈亏
                    if (!CollectionUtils.isEmpty(taskCache.getPositionList())) {
                        StrategyTaskContractCalculateProfitBO profitBo = strategyOrderContractHandlerManager.calculateProfit(taskCache,
                                strategy,
                                product,
                                price);
                        vo.setCurrentProfit(profitBo.getProfit());
                        vo.setCurrentProfitRate(profitBo.getProfitRate());
                    } else {
                        vo.setCurrentProfit(BigDecimal.ZERO);
                        vo.setCurrentProfitRate(BigDecimal.ZERO);
                    }
                }
            }

            // 设置字典项
            vo.setFirstAddSide(StrategyTaskContractEnum.FirstAddSide.findName(vo.getFirstAddSide()));
            vo.setStatus(StrategyTaskContractEnum.Status.findName(vo.getStatus()));
            vo.setErrorSettlementStatus(StrategyTaskContractEnum.ErrorSettlementStatus.findName(vo.getErrorSettlementStatus()));
        }

        return MysqlUtil.transferPage(page, voList);
    }

    /**
     * 任务状态为运行中时，计算下次补仓价格
     *
     * @param strategyList   策略集合
     * @param symbolPriceMap 币对价格
     * @param vo             vo
     * @since 2022/12/21 15:32
     */
    private void getNextAddPrice(List<StrategyContract> strategyList, Map<String, BigDecimal> symbolPriceMap, StrategyTaskContractVO vo) {
        if (!Objects.equals(StrategyTaskContractEnum.Status.RUNNING.getValue(), vo.getStatus())) {
            return;
        }

        // 获取对应的策略
        Optional<StrategyContract> strategyOpt = strategyList.stream()
                .filter(i -> Objects.equals(vo.getStrategyId(), i.getId()))
                .findFirst();
        if (!strategyOpt.isPresent()) {
            return;
        }
        StrategyContract strategy = strategyOpt.get();

        // 达到下单数量时，不再计算下次补仓价格
        if (vo.getAddCount() >= strategy.getOrderNum()) {
            return;
        }
        // 获取任务缓存
        StrategyTaskContractCacheBO taskCache = strategyContractUtil.getStrategyTaskCache(vo.getId(), true);
        // 获取币对价格
        BigDecimal price = symbolPriceMap.get(strategy.getSymbolName());
        if (Objects.isNull(price)) {
            // 查询产品
            SystemProduct product = strategyContractUtil.getProductById(strategy.getProductId(), false);
            if (product.getPlatformId().equals(SystemPlatformConstant.OKX)) {
                price = redisUtils.getSymbolPrice(strategy.getSymbolName(), CommonRedisConstants.OKX + CommonRedisConstants.LAST_PRICE);
            }
            symbolPriceMap.put(strategy.getSymbolName(), price);
        }

        if (!Objects.isNull(taskCache) && !Objects.isNull(price)) {
            // 价格波动比例（%）
            BigDecimal fluctuationRatio = StrategyTaskContractCalculator.fluctuationRatio(strategy.getFluctuationRatio(),
                    strategy.getFluctuationIncrease(),
                    vo.getAddCount());

            // 计算下次补仓价格
            BigDecimal nextAddPrice = StrategyTaskContractCalculator.nextAddPrice(taskCache.getFirstAddSide(),
                    price,
                    taskCache.getLastOpenAvg(),
                    fluctuationRatio);
            // 预估补仓价格
            vo.setNextAddPrice(nextAddPrice.setScale(2, RoundingMode.HALF_UP));
            // 下次补仓比例（%）
            vo.setNextAddRatio(fluctuationRatio.setScale(2, RoundingMode.HALF_UP));
        }
    }

    /**
     * 任务状态为运行中时，计算下次补仓价格
     *
     * @param strategy    策略
     * @param symbolPrice 币对价格
     * @param vo          vo
     * @param taskCache   任务缓存
     * @since 2022/12/21 15:32
     */
    private void getNextAddPrice(StrategyContract strategy, BigDecimal symbolPrice, StrategyTaskContractVO vo, StrategyTaskContractCacheBO taskCache) {
        if (!Objects.equals(StrategyTaskContractEnum.Status.RUNNING.getValue(), vo.getStatus())) {
            return;
        }

        // 达到下单数量时，不再计算下次补仓价格
        if (vo.getAddCount() >= strategy.getOrderNum()) {
            return;
        }

        if (!Objects.isNull(taskCache) && !Objects.isNull(symbolPrice)) {
            // 价格波动比例（%）
            BigDecimal fluctuationRatio = StrategyTaskContractCalculator.fluctuationRatio(strategy.getFluctuationRatio(),
                    strategy.getFluctuationIncrease(),
                    vo.getAddCount());

            // 计算下次补仓价格
            BigDecimal nextAddPrice = StrategyTaskContractCalculator.nextAddPrice(taskCache.getFirstAddSide(),
                    symbolPrice,
                    taskCache.getLastOpenAvg(),
                    fluctuationRatio);
            // 预估补仓价格
            vo.setNextAddPrice(nextAddPrice.setScale(2, RoundingMode.HALF_UP));
            // 下次补仓比例（%）
            vo.setNextAddRatio(fluctuationRatio.setScale(2, RoundingMode.HALF_UP));
        }
    }

    /**
     * 确认异常解决
     *
     * @param dto dto
     * @since 2022/12/10 17:37
     */
    @Override
    public void errorResolve(StrategyContractErrorResolveDTO dto) {
        strategyTaskContractHandler.errorResolve(dto);
    }

}
